#include "gtest/gtest.h"
#include <memory>
#include <string>
#include <iostream>
#include <unordered_map>

#include "recovery/Topic.h"

using namespace recovery;
//  Only Save an Topic(aka. table) to disk for recovery
//  Function:
//  1. storage::loading (path)
//  2. storage->append(kv)->append(kv)
//  3. storage->query_data:  get current newest snapshot , and visit it 
//  ~. storage::unloading
//
//  ThreadSafe:
//  
// Failure:
// process Failure
// Disk Failure 
// Power Failure


/*
class Topics {
public:
    Topics(){
    }
    ~Topics(){
    }

    bool contains(const std::string name) const{
        return topics.find(name) != topics.end();
    }
    Topics & set(const std::string & name, Topic * topic){
        auto iter = topics.find(name);
        if(iter != topics.end()){//replace it
            topics[name] = topic;
        }
        else{
            topics.insert(std::make_pair(name, topic));
        }
        return *this;
    }
    //TODO: extract the Result for return value
    std::pair<bool, Topic *> get(const std::string & name){
        auto iter = topics.find(name);
        if(iter != topics.end()){
            return std::make_pair(true, iter->second);
        }
        return std::make_pair(false, (Topic*)nullptr);
    }
    template<typename Fn>
    int visit(const Fn & fn){
        for(auto & pair : topics){
            if(0 != fn(pair.first, pair.second)){
                return -1; 
            }
        }
        return 0;
    }
private:
    std::unordered_map<std::string, Topic*> topics;
};


class Storage : public IResource{
public:
    static Storage * fromPath(const std::string & path){ 
        auto * result = new Storage(path);
        if(0 == result->open()){
            return result;
        }
        //TODO: Error Info
        return nullptr;
    }
    explicit Storage(const std::string & rootDir)
    :rootDir(rootDir)
    , opened(false)
    {
    }
    ~Storage(){
        close();
    }

    int open() override{
        if(!opened){
            //TODO: load storage meta
            opened = true;
            return 0;
        }
        return -1; //Opened already
    }
    int close() override{
        if(opened){
            //TODO: flush data
            topics.visit([](const std::string & name, Topic * topic) -> int{
                delete topic;
                return 0;
            });
            //TODO: close the storage
            opened = false;
        }
		return 0;
    }
	bool isOpen() const override{
		return opened;
	}

    // create topic
    Topic & getTopic(const std::string & name){
        if(topics.contains(name)){
            return *(topics.get(name).second);
        }
        Topic * topic = new Topic(name, rootDir);
        assert(topic->open() == 0);
        topics.set(name, topic);
        return *topic;
    }
    // looup topic
    Topic * lookupTopic(const std::string & name){
        if(topics.contains(name)){
            return topics.get(name).second;
        }
        return nullptr;
    }

private:
    std::string rootDir;
    bool opened;
    Topics topics;
};

TEST(RecoveryTest, testMain){

    const std::string root="./data";
    std::shared_ptr<Storage> storage(Storage::fromPath(root)); //loading  && auto unloading

    {
        auto & topic = storage->getTopic("topic-name");
        topic.set("key", "value0") //insert key
            .set("key1", "value1") //insert key1
            .set("key2", "value2") //insert key2
            .set("key2", "value3") //update key2
            .commit();

        auto * oldTopic = storage->lookupTopic("topic-name");
        ASSERT_EQ(&topic, oldTopic);
        auto * notExistTopic = storage->lookupTopic("topic-name-no-exist");
        ASSERT_EQ(nullptr, notExistTopic);
        
        // visit ordered pair
        topic.visit([](const std::string &k, const std::string & v) -> int {
            std::cout << k << ":" << v << std::endl;
			return 0;
        });
    }

    ASSERT_TRUE(true);
}
*/
